﻿using System;
using System.Data;
using System.Linq;
using System.Text.RegularExpressions;
using System.Collections.Generic;

namespace Citi.Helpers
{
    public class HtmlTableParser
    {
        private const RegexOptions ExpressionOptions = RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase;

        private const string CommentPattern = "<!--(.*?)-->";
        private const string TablePattern = "<table[^>]*>(.*?)</table>";
        private const string HeaderPattern = "<th[^>]*>(.*?)</th>";
        private const string RowPattern = "<tr[^>]*>(.*?)</tr>";
        private const string CellPattern = "<td[^>]*>(.*?)</td>";

        public static DataSet ParseDataSet(string html)
        {
            DataSet dataSet = new DataSet();
            MatchCollection tableMatches = Regex.Matches(
                WithoutComments(html),
                TablePattern,
                ExpressionOptions);

            foreach (Match tableMatch in tableMatches)
            {
                dataSet.Tables.Add(ParseTable(tableMatch.Value));
            }

            return dataSet;
        }

        public static DataTable ParseTable(string tableHtml)
        {
            string tableHtmlWithoutComments = WithoutComments(tableHtml);

            DataTable dataTable = new DataTable();

            MatchCollection rowMatches = Regex.Matches(
                tableHtmlWithoutComments,
                RowPattern,
                ExpressionOptions);

            //dataTable.Columns.AddRange(tableHtmlWithoutComments.Contains("<th")
            dataTable.Columns.AddRange(true
                                           ? ParseColumns(tableHtml)
                                           : GenerateColumns(rowMatches));

            ParseRows(rowMatches, dataTable);

            return dataTable;
        }

        private static string WithoutComments(string html)
        {
            return Regex.Replace(html, CommentPattern, string.Empty, ExpressionOptions);
        }

        private static void ParseRows(MatchCollection rowMatches, DataTable dataTable)
        {
            foreach (Match rowMatch in rowMatches)
            {
                // if the row contains header tags don't use it - it is a header not a row
                if (!rowMatch.Value.Contains("<th"))
                {
                    DataRow dataRow = dataTable.NewRow();

                    MatchCollection cellMatches = Regex.Matches(
                        rowMatch.Value,
                        CellPattern,
                        ExpressionOptions);

                    for (int columnIndex = 0; columnIndex < cellMatches.Count; columnIndex++)
                    {
                        dataRow[columnIndex] = cellMatches[columnIndex].Groups[1].ToString();
                    }

                    dataTable.Rows.Add(dataRow);
                }
            }
        }

        private static DataColumn[] ParseColumns(string tableHtml)
        {
            MatchCollection headerMatches = Regex.Matches(
                tableHtml,
                HeaderPattern,
                ExpressionOptions);

            //return (from Match headerMatch in headerMatches
            //        select new DataColumn(headerMatch.Groups[1].ToString())).ToArray();

            var columns = new List<DataColumn>();

            for (int i = 0; i < headerMatches.Count; i++)
            {
                var columnName = headerMatches[i].Groups[1].ToString();
                var countColumns = columns.Cast<DataColumn>().Count(x => x.ColumnName.StartsWith(columnName));

                if (countColumns > 0)
                    columnName += countColumns;

                columns.Add(new DataColumn(columnName));
            }

            return columns.ToArray();
        }

        private static DataColumn[] GenerateColumns(MatchCollection rowMatches)
        {
            int columnCount = Regex.Matches(
                rowMatches[0].ToString(),
                CellPattern,
                ExpressionOptions).Count;

            //return (from index in Enumerable.Range(0, columnCount)
            //        select new DataColumn("Column " + Convert.ToString(index))).ToArray();

            var columns = new List<DataColumn>();

            for (int i = 0; i < columnCount; i++)
            {
                var columnName = "Column " + Convert.ToString(i);
                var countColumns = columns.Cast<DataColumn>().Count(x => x.ColumnName.StartsWith(columnName));

                if (countColumns > 0)
                    columnName += countColumns;

                columns.Add(new DataColumn(columnName));
            }

            return columns.ToArray();
        }
    }
}
